﻿using FrameworkCore.Caches;
using Newtonsoft.Json;
using StackExchange.Redis;

namespace Infrastructure.Caches;

#nullable disable

public class RedisCacheService : ICacheService
{
    private readonly IConnectionMultiplexer _redis;

    public RedisCacheService(IConnectionMultiplexer redis)
    {
        _redis = redis ?? throw new ArgumentNullException(nameof(redis));
    }

    public async Task<T> GetAsync<T>(string key)
    {
        var value = await _redis.GetDatabase().StringGetAsync(key);
        return value.HasValue ? Deserialize<T>(value) : default;
    }

    public async Task SetAsync<T>(string key, T value, TimeSpan absoluteExpirationRelativeToNow)
    {
        var serializedValue = Serialize(value);
        await _redis.GetDatabase().StringSetAsync(key, serializedValue, absoluteExpirationRelativeToNow);
    }

    public async Task<bool> RemoveAsync(string key)
    {
        return await _redis.GetDatabase().KeyDeleteAsync(key);
    }

    public async Task<bool> ExistsAsync(string key)
    {
        return await _redis.GetDatabase().KeyExistsAsync(key);
    }

    public async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> valueFactory, TimeSpan absoluteExpirationRelativeToNow)
    {
        var existingValue = await GetAsync<T>(key);
        if (existingValue != null)
        {
            return existingValue;
        }

        var newValue = await valueFactory();
        await SetAsync(key, newValue, absoluteExpirationRelativeToNow);
        return newValue;
    }

    public async Task<T> GetOrCreateAsync<T>(string key, Func<T> valueFactory, TimeSpan absoluteExpirationRelativeToNow)
    {
        var existingValue = await GetAsync<T>(key);
        if (existingValue != null)
        {
            return existingValue;
        }

        var newValue = valueFactory();
        await SetAsync(key, newValue, absoluteExpirationRelativeToNow);
        return newValue;
    }

    private static T Deserialize<T>(RedisValue serializedValue)
    {
        return JsonConvert.DeserializeObject<T>(serializedValue);
    }

    private static RedisValue Serialize<T>(T value)
    {
        return JsonConvert.SerializeObject(value);
    }
}
